//-----------------------------------------------------------------------------
//	File: ssd1306_i2c.c
//	Author: xialing
//		i2c mode dirver for ssd1306
//-----------------------------------------------------------------------------

#include "ssd1306_i2c.h"

// ssd1306 reset pin
#define SSD1306_RCC_ALL		RCC_APB2Periph_GPIOE
#define SSD1306_RST_PORT	GPIOE
#define SSD1306_RST_PIN		GPIO_Pin_0

// ssd1306 hold reset times
#define HOLD_TIMES			1000

// rotation
//#define SSD1306_ROTATION_90
//#define SSD1306_ROTATION_180
//#define SSD1306_ROTATION_270

// ssd1306 i2c address
#define SSD1306_WRITE_ADDR	0x78

// ssd1306 write mode
#define SSD1306_CMD_MODE	0x00
#define SSD1306_DAT_MODE	0x40

// ssd1306 ram
static uint8_t ram[1024];

//-----------------------------------------------------------------------------
//	configure_ssd1306_gpio - lcd ssd1306 gpio configure
//-----------------------------------------------------------------------------
static void configure_ssd1306_gpio(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	// enable the peripheral's clock
	RCC_APB2PeriphClockCmd(SSD1306_RCC_ALL, ENABLE);
	// default state
	SSD1306_RST_PORT->ODR &= ~SSD1306_RST_PIN;
	// mode
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	// reset
	GPIO_InitStructure.GPIO_Pin = SSD1306_RST_PIN;
	GPIO_Init(SSD1306_RST_PORT, &GPIO_InitStructure);
}

//-----------------------------------------------------------------------------
//	ssd1306_cmd - write command to ssd1306
//-----------------------------------------------------------------------------
static void ssd1306_cmd(uint8_t cmd)
{
	i2c_start();
	i2c_write(SSD1306_WRITE_ADDR);
	i2c_write(SSD1306_CMD_MODE);
	i2c_write(cmd);
	i2c_stop();
}

//-----------------------------------------------------------------------------
//	ssd1306_reset - reset the ssd1306
//-----------------------------------------------------------------------------
void ssd1306_reset(void)
{
	uint16_t i = 0;

	SSD1306_RST_PORT->ODR &= ~SSD1306_RST_PIN;
	while (i++ != HOLD_TIMES) {
		__nop();
	}
	SSD1306_RST_PORT->ODR |= SSD1306_RST_PIN;
}

//-----------------------------------------------------------------------------
//	ssd1306_init - ssd1306 initialization
//-----------------------------------------------------------------------------
void ssd1306_init(void)
{
	// config gpio
	configure_i2c_gpio();
	configure_ssd1306_gpio();
	// ssd1306 reset
	ssd1306_reset();
	// lcd clear
	ssd1306_clear();
	// configure ssd1306
	ssd1306_config();
}

//-----------------------------------------------------------------------------
//	ssd1306_config - configure ssd1306
//-----------------------------------------------------------------------------
void ssd1306_config(void)
{
	ssd1306_cmd(0xD5);	// Display Clock Divide Ratio/Oscillator Frequency
	ssd1306_cmd(0x80);
	ssd1306_cmd(0xA8);	// Multiplex Ratio
	ssd1306_cmd(0x3F);
	ssd1306_cmd(0xD3);	// Display Offset
	ssd1306_cmd(0x00);
	ssd1306_cmd(0x40);	// Display Start Line
	ssd1306_cmd(0xA0);	// 0xA1:Segment Re-Map
	ssd1306_cmd(0xC0);	// 0xC8:COM Output Scan Direction
	ssd1306_cmd(0xDA);	// COM Pins Hardware Configuration
	ssd1306_cmd(0x12);
	ssd1306_cmd(0x81);	// Contrast Control
	ssd1306_cmd(0x7F);
	ssd1306_cmd(0xD9);	// Pre-Charge Period
	ssd1306_cmd(0xF1);
	ssd1306_cmd(0xDB);	// VCOMH Deselect Level
	ssd1306_cmd(0x40);
	ssd1306_cmd(0xA4);	// Entire Display On/Off
	ssd1306_cmd(0xA6);	// Normal/Inverse Display
	ssd1306_cmd(0x8D);	// Charge Pump
	ssd1306_cmd(0x14);
	ssd1306_cmd(0x20);	// Memory Addressing Mode
	ssd1306_cmd(0x00);
	// set write area
	ssd1306_cmd(0x21);	// Column
	ssd1306_cmd(0);
	ssd1306_cmd(127);
	ssd1306_cmd(0x22);	// Page
	ssd1306_cmd(0);
	ssd1306_cmd(7);
	// let lcd on
	ssd1306_cmd(0xAF);	// Display On
}
//-----------------------------------------------------------------------------
//	ssd1306_clear - clear the lcd
//-----------------------------------------------------------------------------
void ssd1306_clear(void)
{
	uint16_t i;

	for (i=0; i<1024; i++) {
		ram[i] = 0;
	}
}

//-----------------------------------------------------------------------------
//	ssd1306_refresh - refresh ram data to ssd1306
//-----------------------------------------------------------------------------
void ssd1306_refresh(void)
{
	uint16_t i;

	// set start position
	ssd1306_cmd(0x00); // low 4bit x address
	ssd1306_cmd(0x10); // low 3bit x address
	ssd1306_cmd(0xB0); // y page address
	// write data
	i2c_start();
	i2c_write(SSD1306_WRITE_ADDR);
	i2c_write(SSD1306_DAT_MODE);
	for (i=0; i<1024; i++) {
		i2c_write(ram[i]);
	}
	i2c_stop();
}

//-----------------------------------------------------------------------------
//	ssd1306_draw_pixel - draw a pixel
//-----------------------------------------------------------------------------
void ssd1306_draw_pixel(uint8_t x, uint8_t y, uint8_t c)
{
	// rotation
#ifdef SSD1306_ROTATION_90
	uint8_t temp = x;
	x = y;
	y = 63 - temp;
#elif defined SSD1306_ROTATION_180
	x = 127 - x;
	y = 63 - y;
#elif defined SSD1306_ROTATION_270
	uint8_t temp = y;
	y = x;
	x = 127 - temp;
#endif

	// x & y value assert
	if ((x > 127) || (y > 63)) return;

	// change one bit
	switch (c) {
		case 0: ram[x + ((y >> 3) << 7)] &= ~(1 << (y & 7)); break;
		case 1: ram[x + ((y >> 3) << 7)] |= (1 << (y & 7)); break;
		case 2: ram[x + ((y >> 3) << 7)] ^= (1 << (y & 7)); break;
	}
}

//-----------------------------------------------------------------------------
//	ssd1306_get_pixel - get a pixel
//-----------------------------------------------------------------------------
uint8_t ssd1306_get_pixel(uint8_t x, uint8_t y)
{
	// rotation
#ifdef SSD1306_ROTATION_90
	uint8_t temp = x;
	x = y;
	y = 63 - temp;
#elif defined SSD1306_ROTATION_180
	x = 127 - x;
	y = 63 - y;
#elif defined SSD1306_ROTATION_270
	uint8_t temp = y;
	y = x;
	x = 127 - temp;
#endif

	// x & y value assert
	if ((x > 127) || (y > 63)) return 0;

	// return the bit status
	return (ram[x + ((y >> 3) << 7)] & (1 << (y & 7))) ? 1u : 0u;
}

//-----------------------------------------------------------------------------
//	example
//-----------------------------------------------------------------------------
/*{
	uint8_t x,y,temp;

	ssd1306_init();
	for (y=0; y<64; y++) {
		for (x=0; x<128; x++) {
			temp = ssd1306_get_pixel(x, y);
			ssd1306_draw_pixel(x, y, !temp);
			ssd1306_refresh();
		}
	}
}*/

